Write Ahead Log

    884
    最后修改于

    ACID transactions and implementation in a PostgreSQL Database - Aviator Blog

    WAL 提供了一种机制,对数据库的任何写入,都需要先写入 WAL 文件,后续再将脏页写入缓冲区,写入磁盘。这类似于 Redis 中的 AOF 机制。
    这可以保证 ACID 中的 Durability

    loading...

    SQLite#

    sqlite | Write-Ahead Logging
    SQLite 中有 Rollback 机制实现类似的事情。其通过将数据库原始内容写入 rollback 文件,将更改直接写入数据库文件。
    SQLite 于 3.7.0 版本开始提供 WAL 机制,与 rollback 相反,原始数据仍在数据库中,将更改追加到 WAL 文件中。
    这样可以使得原始数据库不被修改,允许其他的读线程进行并发读取。

    Postgres#

    PostgreSQL: Documentation: 16: 30.3. Write-Ahead Logging (WAL)
    WAL: Everything you want to know - YouTube
    在没有 WAL 或类似机制的时候,DB 的修改流程如下图所示,当在第四步出现崩溃时,就会出现数据丢失。

    检查点#

    最终 WAL 中修改的数据还是要写回到数据库文件的。将 WAL 中的事务写入数据库的点称为检查点。
    默认情况下,在 WAL 大小达到 1000 页的时候会自动执行检查点。(有需要也可以主动执行)

    并发#

    WAL 模式下,开启一个读操作时,首先定位到WAL 中最后一个合法 commit 的位置,称为结束标记。每个读线程都可以有自己的结束标记。在一个读线程的读取事务中,其所看到的内容不变(结束标记不变)。

    当读取器需要一页内容时,它首先检查 WAL 中是否存在该页,如果是,它会提取 WAL 中结束标记之前该页面的最后一个副本。如果在结束标记前 WAL 中不存在该页面的副本,则从原始数据库文件中读取该页面。

    因为读线程相互独立,为了避免每个读线程都扫描整个 WAL 文件,内存中还维护了一个 共享的 wal-index 数据结构,可以让读线程快速定位页面,减少 I/O。(这要求所有的读线程位于同一个机器上,因此 WAL 不能用于 NFS)

    检查点执行时,将 WAL 文件中的内容同步到数据库文件。但是当检查点执行必须停止于当前读线程中最早的那个结束标记,防止覆盖读线程正在使用的部分。在 wal-index 中会记录同步中断的位置,在下一次检查点时继续同步。

    loading...

    因此长时间运行的读事务会阻塞检查点线程,但最终读事务会结束,检查点线程可以继续同步。发生写操作时,写线程查看检查点的同步进度。如果所有 WAL 内容都同步到了数据库且不存在使用 WAL 的读线程,写线程会将 WAL 清空,再开始写入新事务。可以防止 WAL 无限增长。

    瓶颈#

    因为其对并发的限制,在写入密集型的应用中,性能会受到制约,在现代 SSD 中,这种限制显得尤为明显。
    MySQL :: MySQL 8.0:新的无锁、可扩展的 WAL 设计

    • 🥳0
    • 👍0
    • 💩0
    • 🤩0